//
//
//      Copyright Timothy Evans 2015    timothy.evans@gsecraif.org
//
//      This file is part of gsecraif.
//
//      gsecraif is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 3 of the License, or
//      (at your option) any later version.
//
//      See gsecraif.cpp
//
//
// =====================================================================
//
//! Functions to process a matrix of bits.
//
// =====================================================================
//
// Compile with g++

#include "bitmatrix.h"

using namespace std;

// functions to process bit matrix:

unsigned int Bitmatrix::getrows() {
    return bit_rows;
};

// =====================================================================

unsigned int Bitmatrix::getcols() {
    return bit_columns;
};

// =====================================================================

unsigned int Bitmatrix::info() {
    return compact_bytes.size();
};

// =====================================================================

void Bitmatrix::getcompactbytes(vector<unsigned char> &bytes) {
    bytes = compact_bytes;
};

// =====================================================================

void Bitmatrix::setcompactbytes(vector<unsigned char> bytes) {
    compact_bytes = bytes;
};

// =====================================================================

int Bitmatrix::getexpandbytecols() {
    int expandedbytecols = 0; // columns of bytes
    expandedbytecols = getcols() / 8;
    if (getcols() % 8) ++expandedbytecols;
    return expandedbytecols;
};

// =====================================================================

int Bitmatrix::getexpandrows() {
    int expandedbyterows = 0; // rows of bytes
    // in expanded format, number of byte rows = number of bit rows
    expandedbyterows = getrows();
    return expandedbyterows;
};

// =====================================================================

void Bitmatrix::getexpandbytes( vector<vector<unsigned char> >  &bytes) {
    unsigned int totalbits = 0; // total bits in matrix
    unsigned int expandedrows = 0; // rows of bytes
    unsigned int expandedbytecols = 0; // columns of bytes
    unsigned int current_comp_byte = 0; // 0 - n-1 where n is the total
                                        // number of compact bytes
    unsigned int current_comp_bit = 0; // 0-7 bit in compact byte
    unsigned int current_row = 0; // 0- n-1 where n is the total number of rows
    unsigned char current_row_bit = 0; // bit in expanded row -  bit cols -1;
    unsigned int current_exp_byte = 0; // 0 - n-1 where n i the total
                                       // number of expanded bytes in a row
    unsigned char current_exp_bit = 0; // bit in expanded byte
    unsigned int current_bit = 0; // 0 to n-1 where n is the total
                                  // number of bits
    unsigned int bit_column = 0; // 0 to n-1 where n is the number of
                                 // bit columns
    vector< vector<unsigned char> > leftexpandedbytes;
    vector<unsigned char> comp_bytes;
    expandedbytecols = getexpandbytecols();
    expandedrows = getexpandrows();
    if ((expandedbytecols == 0) || (expandedrows == 0)) {
        return;
        }
    // initialize:
    leftexpandedbytes.resize( expandedrows ,
                              vector<unsigned char>( expandedbytecols , 0 ));
    getcompactbytes(comp_bytes); // get the compact bytes

    totalbits = getcols() * getrows();

    for (unsigned int bit=0; bit != totalbits; ++bit) {
        current_comp_byte = bit / 8;  // compact byte that holds the current bit
        current_comp_bit = bit % 8;   // bit within the compact byte that
                                      // holds the current bit
        current_row = bit / getcols();        // row containing current bit
        current_row_bit = bit % getcols();
        current_exp_byte = current_row_bit / 8;
        current_exp_bit = current_row_bit % 8;

        // bitwise AND is used here to determine setting of a bit
        if ( (comp_bytes[current_comp_byte] & masks[current_comp_bit]) ) {
            // compact bit is 1
            // bitwise AND is used here to determine setting of a bit
            // below, the & is not part of a logical test;
            // the expression in inner parenthesise is evaluated
            // as true or false
            if ( (leftexpandedbytes[current_exp_byte][current_row] &
                  masks[current_exp_bit]) ) {
                // expanded bit is 1 no change needed
                 }
            else {
                // expanded bit is 0, set to 1 with arithemtic add
                leftexpandedbytes[current_exp_byte][current_row] =
                leftexpandedbytes[current_exp_byte][current_row] +
                masks[current_exp_bit];
                }
            }
        else {
            // compact bit is 0
            // bitwise AND is used here to determine setting of a bit
            // below, the & is not part of a logical test;
            // the expression in inner parenthesise is evaluated
            // as true or false
            if ( (leftexpandedbytes[current_exp_byte][current_row] &
                  masks[current_exp_bit]) ) {
                // expanded bit is 1, set to 0 with arithmetic subtract
                leftexpandedbytes[current_exp_byte][current_row] =
                leftexpandedbytes[current_exp_byte][current_row] -
                masks[current_exp_bit];
                }
            else {
                // expanded bit is 0 no change needed
                }
            } // if ( (comp_bytes

        if ( (leftexpandedbytes[current_exp_byte][current_row] &
              masks[current_exp_bit]) ) {
            //cerr << "1";
            }
        else {
            //
            }
        //
        }  // for


    for (unsigned int row=0; row != expandedrows; ++row) {
        for (unsigned int bytecol=0; bytecol != expandedbytecols; ++bytecol) {
            bytes[bytecol][row] = leftexpandedbytes[bytecol][row];
            }
        }

};

// =====================================================================

void Bitmatrix::compact( vector<vector<unsigned char> >  leftexpandedbytes) {
 // take a left aligned expanded bytes matrix and compact it in to a matrix
    unsigned int expandedrows = 0; // rows of bytes
    unsigned int expandedbytecols = 0; // columns of bytes
    unsigned int suppliedrows = 0; // rows of bytes in expanded matirx
    unsigned int suppliedcols = 0; // columns of bytes in expanded matrix
    unsigned int current_comp_byte = 0; // 0 - n-1 where n is the total
                                        // number of compact bytes
    unsigned char current_comp_bit = 0; // 0-7 bit in compact byte
    unsigned int current_row = 0; // 0- n-1 where n is the total number of rows
    unsigned int current_row_bit = 0; // bit in expanded row - cols -1;
    unsigned int current_exp_byte = 0; // 0 - n-1 where n i the total
                                       // number of expanded bytes in a row
    unsigned char current_exp_bit = 0; // bit in expanded byte
    vector<unsigned char> comp_bytes;
    expandedbytecols = getexpandbytecols();


    expandedrows = getexpandrows();
    if ((expandedbytecols == 0) || (expandedrows == 0)) {
        cerr << "byte rows: " << expandedrows << "\n";
        cerr << "byte cols: " << expandedbytecols << "\n";
        return;
        }
    // if these do not match the expanded bytes matrix, then exit

    suppliedcols = leftexpandedbytes.size();
    suppliedrows = leftexpandedbytes[0].size();

    if ((suppliedrows == 0) || (suppliedcols == 0)) {
        cerr << "supplied rows: " << suppliedrows << "\n";
        cerr << "supplied cols: " << suppliedcols << "\n";
        return;
        }

    if ((suppliedrows == expandedrows) && (suppliedcols == expandedbytecols)) {

        // initialize:
        comp_bytes.resize( info() );

        for (unsigned int bit=0; bit != (getcols() * getrows()); ++bit) {
            current_comp_byte = bit / 8;
            current_comp_bit = bit % 8;
            current_row = bit / getcols();
            current_row_bit = bit % getcols();
            current_exp_byte = current_row_bit / 8;
            current_exp_bit = current_row_bit % 8;
            // bitwise AND is used here to determine setting of a bit
            if ( (leftexpandedbytes[current_exp_byte][current_row] &
                  masks[current_exp_bit]) ) {
                // expanded bit is 1
                // bitwise AND is used here to determine setting of a bit
                // below, the & is not part of a logical test;
                // the expression in inner parenthesise is evaluated
                // as true or false
                if ( (comp_bytes[current_comp_byte] &
                      masks[current_comp_bit]) ) {
                    // compact bit is 1 no need to change
                    }
                else {
                    // compact bit is 0, set to 1 with arithemtic add
                    comp_bytes[current_comp_byte] =
                    comp_bytes[current_comp_byte] + masks[current_comp_bit];
                    }
                 }
            else {
                // expanded bit is 0
                if ( (comp_bytes[current_comp_byte] &
                      masks[current_comp_bit]) ) {
                    // compact bit is 1, set to 0 with arithmetic subtract
                    cerr << "true";
                    cerr << "\n";
                    comp_bytes[current_comp_byte] =
                    comp_bytes[current_comp_byte] - masks[current_comp_bit];
                    }
                else {
                    // compact bit is 0 no need to change
                    }
                } // if ( (leftexpandedbytes

            } // for (unsigned int bit=0;...

        setcompactbytes(comp_bytes);
    } // if ((suppliedrows == expandedrows...

};

// =====================================================================

void Bitmatrix::transpose( Bitmatrix &transposed)  {
// transpose matrix: bytes are overwritten with the transposed values of
// leftexpanded bytes

    unsigned int origbitrows = 0;
    unsigned int origbitcols = 0;
    unsigned int transbitrows = 0;
    unsigned int transbitcols = 0;

    unsigned int origrowbit = 0; // 0 - origbitrows -1
    unsigned int origcolbit = 0;
    unsigned int transrowbit = 0; // 0 - transbitrows -1
    unsigned int transcolbit = 0;

    unsigned int origbit = 0; // 0 - totalbits -1
    unsigned int transbit = 0; // 0 - totalbits -1

    unsigned int orig_comp_byte = 0; // 0 - n-1 where n is the total
                                     // number of compact bytes
    unsigned int trans_comp_byte = 0; // 0 - n-1 where n is the total
                                      // number of compact bytes

    unsigned char current_orig_comp_bit = 0; // 0-7 bit in compact byte
    unsigned char current_trans_comp_bit = 0; // 0-7 bit in compact byte

    vector<unsigned char> orig_comp_bytes;
    vector<unsigned char> trans_comp_bytes;

    origbitrows = getrows();
    origbitcols = getcols();
    transbitrows = transposed.getrows();
    transbitcols = transposed.getcols();

    if ((origbitrows != transbitcols) || (origbitcols != transbitrows)) {
        cerr << "Can't transpose \n";
        return;
        }

    orig_comp_bytes.resize( info() );
    trans_comp_bytes.resize( transposed.info() );

    getcompactbytes(orig_comp_bytes); // get the compact bytes
    transposed.getcompactbytes(trans_comp_bytes); // get the compact bytes

    //cerr << "transpose \n";

    for (origrowbit=0; origrowbit != origbitrows; ++origrowbit) {
        for (origcolbit=0; origcolbit != origbitcols; ++origcolbit) {
            transrowbit = origcolbit;
            transcolbit = origrowbit;

            transbit = (transrowbit * transbitcols) + transcolbit;

            orig_comp_byte = origbit / 8;
            trans_comp_byte = transbit / 8;
            current_orig_comp_bit = origbit % 8;
            current_trans_comp_bit = transbit % 8;
            // bitwise AND is used here to determine setting of a bit
            // below, the & is not part of a logical test;
            // the expression in inner parenthesise is evaluated
            // as true or false
            if ( (orig_comp_bytes[orig_comp_byte] &
                  masks[current_orig_comp_bit]) ) {
                // orig bit is 1
                if ( (trans_comp_bytes[trans_comp_byte] &
                      masks[current_trans_comp_bit]) ){
                    // trans bit is 1; no change needed
                    }
                else {
                    // bit is 0, set to 1 with arithemtic add
                    trans_comp_bytes[trans_comp_byte] =
                    trans_comp_bytes[trans_comp_byte] +
                    masks[current_trans_comp_bit];
                    }
                }
            else {
                // orig bit is 0
                if ( (trans_comp_bytes[trans_comp_byte] &
                      masks[current_trans_comp_bit]) ){
                    // trans bit is 1; set to 0 with arithmetic subtract
                    cerr << "y ";
                    trans_comp_bytes[trans_comp_byte] =
                    trans_comp_bytes[trans_comp_byte] -
                    masks[current_trans_comp_bit];
                    }
                else {
                    // trans bit is 0; no change needed
                    }
                }
            ++origbit;
            } // for (origcolbit...
        } // for (origrowbit...
    // set transposed matrix compact bytes to contain comp_bytes:
    transposed.setcompactbytes(trans_comp_bytes);


};

// =====================================================================
